﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Program.cs" company="Jared Schlicht">
//   Copyright (c) 2011 Jared Schlicht.
// </copyright>
// <summary>
//   Contains the program entry point.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace JSchlicht.SqlCsv
{
    using System;
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Data.SqlClient;
    using System.IO;

    using NDesk.Options;

    /// <summary>
    /// Contains the program entry point. Reads command line arguments, executes the provided sql queries, and writes
    /// the results as CSV data.
    /// </summary>
    public static class Program
    {
        #region Methods

        /// <summary>
        /// Defines the program entry point.
        /// </summary>
        /// <param name="args">
        /// An array of <see cref="T:System.String"/> containing command line parameters.
        /// </param>
        public static void Main(string[] args)
        {
            // Specifies the connection string to use to connect to the SQL server.
            string connectionString = null;

            /* If set to true, an explanation of how to use the program will be shown. If any command line arguments
             * were invalid, this variable should be set to true so that the user is shown how to properly use the
             * program.
             */
            bool showHelp = false;

            // A list of SQL queries that the user would like to execute. These will not be validated for correctness.
            List<string> sqlQueries;

            // Optional, CSV data will be written to this file if it specified. Otherwise, data will be written to standard out.
            string outputFilename = null;

            string databaseType = null;

            // Allows the user to override the timeout for the sql command
            int timeout = 30;

            // Uses the NDesk Options library to specify what command line arguments the program will accept.
            OptionSet options = new OptionSet
                {
                    { "c|connectionString=", "specifies which {CONNECTIONSTRING} to use to connect to the SQL Server", v => { connectionString = v; } },
                    { "t|timeout=", "number of seconds before a sql command will time out. If set to 0, timeout is disabled. Default: 30", v => { timeout = int.Parse(v); } },
                    { "h|help", "documents the usage of this program", v => { showHelp = v != null; } }, 
                    { "o|outputFile=",  "the csv data will be written to this file. If not specified, csv data will be written to standard output", v => { outputFilename = v; } },
                    { "d|dbType=", "specifies what type of database to connect to. Options: mysql, oracle, postgresql, sqlite, sqlserver. If not specified, sqlserver will be assumed.", v => { databaseType = v; } }
                };

            try
            {
                sqlQueries = options.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("sqlcsv: ");
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `sqlcsv --help' for more information.");
                return;
            }

            // A SQL connection string is absolutely required.
            if (connectionString == null)
            {
                Console.WriteLine("sqlcsv: No connection string was specified.");
                ShowHelp(options);
                return;
            }

            if (showHelp)
            {
                ShowHelp(options);
                return;
            }

            DbConnection connection;
            switch (databaseType)
            {
                case "mysql":
                    connection = new MySql.Data.MySqlClient.MySqlConnection(connectionString);
                    break;
                case "postgresql":
                    connection = new Npgsql.NpgsqlConnection(connectionString);
                    break;
                case "sqlite":
                    connection = new System.Data.SQLite.SQLiteConnection(connectionString);
                    break;
                case "oracle":
                    connection = new System.Data.OracleClient.OracleConnection(connectionString);
                    break;
                default:
                    connection = new SqlConnection(connectionString);
                    break;
            }

            TextWriter output = outputFilename != null ? new StreamWriter(outputFilename, false) : Console.Out;

            using (QueryResultWriter resultWriter = new QueryResultWriter(output))
            {
                using (QueryRunner queryRunner = new QueryRunner(connection, timeout))
                {
                    foreach (string sqlQuery in sqlQueries)
                    {
                        IEnumerable<string[]> records = queryRunner.RunQuery(sqlQuery);
                        resultWriter.Write(records);
                    }
                }
            }
        }

        /// <summary>
        /// Shows a help message documenting the proper usage of this program.
        /// </summary>
        /// <param name="p">
        /// A set of all valid command line arguments for this program.
        /// </param>
        private static void ShowHelp(OptionSet p)
        {
            Console.WriteLine("Usage: sqlcsv -c connectionString [-o outputFile] sqlCommand...");
            Console.WriteLine("Executes the provided sql commands and outputs the results as CSV file.");
            Console.WriteLine("SQL commands and connection strings should be surrounded by quotation marks (\"\")");
            Console.WriteLine();
            Console.WriteLine("Options:");
            p.WriteOptionDescriptions(Console.Out);
        }

        #endregion
    }
}